home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / util / bsindex1 / part02 < prev    next >
Internet Message Format  |  1990-02-02  |  59KB

  1. Path: xanth!cs.odu.edu!Amiga-Request
  2. From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v90i042: BBSindex 1.0 - file database utility for BBS-PC!, Part02/03
  5. Message-ID: <11249@xanth.cs.odu.edu>
  6. Date: 2 Feb 90 19:56:06 GMT
  7. Sender: tadguy@cs.odu.edu
  8. Reply-To: Eddy Carroll <ECARROLL%vax1.tcd.ie@CUNYVM.CUNY.EDU>
  9. Lines: 2326
  10. Approved: tadguy@cs.odu.edu (Tad Guy)
  11. X-Mail-Submissions-To: Amiga@cs.odu.edu
  12.  
  13. Submitted-by: Eddy Carroll <ECARROLL%vax1.tcd.ie@CUNYVM.CUNY.EDU>
  14. Posting-number: Volume 90, Issue 042
  15. Archive-name: util/bbsindex-1.0/part02
  16.  
  17. #!/bin/sh
  18. # This is a shell archive.  Remove anything before this line, then unpack
  19. # it by saving it into a file and typing "sh file".  To overwrite existing
  20. # files, type "sh file -c".  You can also feed this as standard input via
  21. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  22. # will see the following message at the end:
  23. #        "End of archive 2 (of 3)."
  24. # Contents:  src/bbsindex.c src/command.c src/format.c
  25. # Wrapped by tadguy@xanth on Fri Feb  2 14:54:38 1990
  26. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  27. if test -f 'src/bbsindex.c' -a "${1}" != "-c" ; then 
  28.   echo shar: Will not clobber existing file \"'src/bbsindex.c'\"
  29. else
  30. echo shar: Extracting \"'src/bbsindex.c'\" \(13333 characters\)
  31. sed "s/^X//" >'src/bbsindex.c' <<'END_OF_FILE'
  32. X/*
  33. X * BBSindex                         (C) Copyright Eddy Carroll, August 1989
  34. X *
  35. X * This is a utility which will scan the file catalogue of BBS-PC! and
  36. X * build lists of the files in various sections and directories.
  37. X *
  38. X * It is designed to be run automatically on a regular basis in the
  39. X * background. It reads in instructions from an input file, and 
  40. X * produces output files based on these instructions.
  41. X *
  42. X * Usage: BBSindex [options] [scriptfile] ..
  43. X *
  44. X * With no parameters, a short help message is displayed.
  45. X *
  46. X * The possible options are:
  47. X *
  48. X * -c <file>        This specifies the configuration file (CFGINFO.DAT)
  49. X * -d <file>        This specifies the input file (default UDHEAD.DAT)
  50. X * -f "string"         This specifies the format string for the output
  51. X * -h                Displays the help message
  52. X * -n                Stops AmigaDOS putting up requesters
  53. X * -t               Trace mode - display each line before execution
  54. X *
  55. X * scriptfile is a file containing commands to control what is output.
  56. X * See the documentation for more info. Note that options and script files
  57. X * may be freely interspersed.
  58. X *
  59. X */
  60. X
  61. X#define MAIN
  62. X
  63. X#ifndef LATTICE_50
  64. X#include "system.h"
  65. X#endif
  66. X
  67. X#include "bbsindex.h"
  68. X
  69. X/*
  70. X *        Miscellaneous static global variables
  71. X */
  72. X
  73. Xstatic struct IntuitionBase *IntuitionBase;
  74. Xstatic BPTR infile, lock;
  75. Xstatic struct Remember *memkey;
  76. Xstatic char buffer[BUFSIZE];
  77. Xstatic int bufpos = 0;
  78. Xstatic APTR requeststat;    /* Ptr to window for AmigaDos requesters */
  79. X
  80. X/*
  81. X *        print()
  82. X *        -------
  83. X *        Prints a string to standard output
  84. X */
  85. Xvoid print(s)
  86. Xchar *s;
  87. X{
  88. X    Write(errorfile,s,strlen(s));
  89. X}
  90. X
  91. X/*
  92. X *        Cleanup()
  93. X *        ---------
  94. X *        Releases all program resources, and exits. Note that because of
  95. X *        the way it checks to see if a resource is allocated, you need to
  96. X *        set pointers to NULL if you release the object associated with
  97. X *        them. For example, when you close a file, remember to set the
  98. X *        file handle to NULL immediately afterwards, otherwise Cleanup()
  99. X *        will try to close it again.
  100. X */
  101. Xvoid Cleanup(code)
  102. Xint code;
  103. X{
  104. X    static ctrlc;
  105. X    struct Process *me = (struct Process *)FindTask(0L);
  106. X    int i;
  107. X
  108. X    if (!ctrlc) {
  109. X        ctrlc = 1;
  110. X        me->pr_WindowPtr = requeststat;
  111. X        if (script)
  112. X            FreeMem(script, scriptsize);
  113. X        for (i = 0; i < nummacros; i++)
  114. X            FreeMem(macros[i], MACROSIZE + macros[i]->size);
  115. X        for (i = 0; i < nestlevel; i++)
  116. X            FreeMem(params[i], PARAMSIZE + params[i]->size);
  117. X        if (memkey)
  118. X            FreeRemember(&memkey, TRUE);
  119. X        if (IntuitionBase)
  120. X            CloseLibrary(IntuitionBase);
  121. X        if (lock)
  122. X            UnLock(lock);
  123. X        if (dirlock)
  124. X            UnLock(dirlock);
  125. X        if (infile)
  126. X            Close(infile);
  127. X        if (outfile != Output())
  128. X            Close(outfile);
  129. X        if (errorfile)
  130. X            Close(errorfile);
  131. X        exit(code);
  132. X    }
  133. X}
  134. X
  135. X/*
  136. X *        cxovf()
  137. X *        -------
  138. X *        The standard lattice "stack abort" code. This is called when the
  139. X *        stack overflows (having first reset the stack pointer to something
  140. X *        safe of course).
  141. X */
  142. Xvoid cxovf()
  143. X{
  144. X    print("\nBBSindex: Stack overflow! Use the STACK command "
  145. X          "to increase stack size\n");
  146. X    Cleanup(32);
  147. X}
  148. X
  149. X/*
  150. X *        chkabort()
  151. X *        ----------
  152. X *        My own version of chkabort(), called by the Lattice Library
  153. X *        functions periodically to check for CTRL-C.
  154. X *
  155. X *        This version cleans up first (otherwise, after CTRL-C'ing a few times
  156. X *        while  processing a 220K UDHEAD.DAT file, you find you don't have any
  157. X *        memory left!).
  158. X */
  159. Xvoid chkabort()
  160. X{
  161. X    if (SetSignal(0,0) & SIGBREAKF_CTRL_C) {
  162. X        SetSignal(0,0xffffffff);
  163. X        print("^C\n");
  164. X        Cleanup(5);
  165. X    }
  166. X}
  167. X
  168. X
  169. X/*
  170. X *        SafeAllocMem()
  171. X *        --------------
  172. X *        A revised AllocMem() that checks for out of memory.
  173. X */
  174. Xvoid *SafeAllocMem(size)
  175. Xint size;
  176. X{
  177. X    void *ptr = AllocMem(size, 0);
  178. X    if (!ptr) {
  179. X        print("BBSindex: Out of memory!\n");
  180. X        Cleanup(20);
  181. X    }
  182. X    return (ptr);
  183. X}
  184. X
  185. X
  186. X/*
  187. X *        mymalloc()
  188. X *        ----------
  189. X *        My own private malloc. No memory limits, and no library overhead.
  190. X *        Easy to replace the call to AllocRemember with one to malloc for
  191. X *        portability. To try and keep down memory fragmentation, requests
  192. X *        for memory less than 200 bytes are allocated from a larger block
  193. X *        which is allocated "on the fly".
  194. X */
  195. Xvoid *mymalloc(size)
  196. Xint size;
  197. X{
  198. X    static void *bigblock;                /* Current large memory block */
  199. X    static int curpos = FRAGBLOCK;        /* Current position in block  */
  200. X    void *ptr;
  201. X    
  202. X    size = (size + 7) & 0xfffffff8;        /* Round up to 8 block boundary */
  203. X    if (size < FRAGTHRESH) {
  204. X        /*
  205. X         *        If we want a small chunk, allocate it from our intermediate
  206. X         *        block.
  207. X         */
  208. X        if ((size + curpos) > FRAGBLOCK) {
  209. X            bigblock = mymalloc(FRAGBLOCK);
  210. X            curpos = 0;
  211. X        }
  212. X        ptr = (char *)bigblock + curpos;
  213. X        curpos += size;
  214. X    } else {
  215. X        /*
  216. X         *        Otherwise, just allocate memory as normal.
  217. X         */
  218. X        ptr = AllocRemember(&memkey, size, 0);
  219. X        if (!ptr) {
  220. X            print("BBSindex: Out of memory!\n");
  221. X            Cleanup(20);
  222. X        }
  223. X    }
  224. X    return (ptr);
  225. X}
  226. X
  227. X
  228. X/*
  229. X *        dumpdata()
  230. X *        ----------
  231. X *        Outputs raw data to a file, checking for write errors.
  232. X */
  233. Xvoid dumpdata(buf,len)
  234. Xchar *buf;
  235. Xint len;
  236. X{
  237. X    if (Write(outfile, buf, len) != len) {
  238. X        print("BBSindex: Error writing output file (disk full?)\n");
  239. X        Cleanup(10);
  240. X    }
  241. X}
  242. X
  243. X/*
  244. X *        putstring()
  245. X *        -----------
  246. X *        This function outputs a string to the current output file. If the
  247. X *        file is a tty, the string is output immediately, else it is
  248. X *        buffered up and output when it is big enough.
  249. X */
  250. X
  251. Xvoid putstring(s)
  252. Xchar *s;
  253. X{
  254. X    int len = strlen(s);
  255. X
  256. X    if (toscreen) {
  257. X        dumpdata(s, len);
  258. X    } else {
  259. X        if ( (bufpos + len) > BUFSIZE) {
  260. X            dumpdata(buffer, bufpos);
  261. X            bufpos = 0;
  262. X        }
  263. X        strcpy(buffer+bufpos, s);
  264. X        bufpos += len;
  265. X    }
  266. X}
  267. X
  268. X/*
  269. X *        flushout()
  270. X *        ----------
  271. X *        Flushes output to disk, before closing a file.
  272. X */
  273. X
  274. Xvoid flushout()
  275. X{
  276. X    if (bufpos > 0) {
  277. X        dumpdata(buffer, bufpos);
  278. X    }
  279. X    bufpos = 0;
  280. X}
  281. X
  282. X/*
  283. X *        openfile()
  284. X *        ----------
  285. X *        Opens a specified file for reading, and returns the length
  286. X *        of the file. If an error occurs, aborts automatically. The
  287. X *        caller has responsibility for calling Close(infile) when finished
  288. X *        with the file.
  289. X */
  290. Xlong openfile(filename)
  291. Xchar *filename;
  292. X{
  293. X    if ((lock = Lock(filename, ACCESS_READ)) == NULL) {
  294. X        print3("BBSindex: Can't access file ",filename,"\n");
  295. X        Cleanup(5);
  296. X    }
  297. X
  298. X    if (!Examine(lock, fib)) {
  299. X        print3("BBSindex: Can't get info for file ",filename,"\n");
  300. X        Cleanup(5);
  301. X    }
  302. X    UnLock(lock); lock = NULL;
  303. X
  304. X    if ((infile = Open(filename, MODE_OLDFILE)) == NULL) {
  305. X        print3("BBSindex: Can't open file ",filename," for input\n");
  306. X        Cleanup(5);
  307. X    }
  308. X    return (fib->fib_Size);
  309. X}
  310. X
  311. X/*
  312. X *        readdatabase()
  313. X *        --------------
  314. X *        This function reads in the entire file database into memory,
  315. X *        allocating memory as required. It sets up an array of pointers to
  316. X *        the records in the database, which is used by qsort() among other
  317. X *        things.
  318. X *
  319. X *        Note that the memory needed is allocated in a number of small
  320. X *        chunks each BLOCKSIZE * UDSIZE bytes in size (~16K by default).
  321. X *        This means there doesn't need to be a contigous block of memory
  322. X *        big enough to hold the whole file database available - it is
  323. X *        split into smaller chunks instead.
  324. X */
  325. X
  326. Xvoid readdatabase(filename)
  327. Xchar *filename;
  328. X{
  329. X    long    size;
  330. X    long    bsize;
  331. X    long    i,j;
  332. X    UDHEAD    *block;        /* Pointer to block of headers    */
  333. X    UDHEAD    **p;
  334. X
  335. X    readfiles = TRUE;
  336. X
  337. X    size = openfile(filename);
  338. X    if ((size % UDSIZE) || (size < (2 * UDSIZE))) {
  339. X        print3("BBSindex: ",filename,
  340. X               " isn't a valid BBS-PC! file header file!\n");
  341. X        Cleanup(5);
  342. X    }
  343. X
  344. X    /*
  345. X     *        File successfully opened, now read in file data.
  346. X     *        Skip past unneeded data at start of BBS-PC! file
  347. X     */
  348. X    Seek(infile, UDSIZE * 2, OFFSET_BEGINNING);
  349. X    size        = size - (2 * UDSIZE);
  350. X    numrecs        = size/UDSIZE;
  351. X    ptrblock    = mymalloc(numrecs * sizeof(UDHEAD *));
  352. X    p            = ptrblock;
  353. X
  354. X    /*
  355. X     *    Now allocate blocks to hold data, read in data, and setup initial
  356. X     *    pointers to point to the file headers.
  357. X     */
  358. X    for (i = numrecs; i > 0; i = i - BLOCKSIZE) {
  359. X
  360. X        bsize = (i > BLOCKSIZE ? BLOCKSIZE : i);
  361. X        block = mymalloc(bsize * UDSIZE);
  362. X
  363. X        chkabort();
  364. X        if (Read(infile, block, bsize * UDSIZE) != (bsize * UDSIZE)) {
  365. X            print3("BBSindex: Error reading from file",filename,"\n");
  366. X            Cleanup(10);
  367. X        }
  368. X
  369. X        /*
  370. X         *        Now initialise all the pointers for this block. Also
  371. X         *        null-terminate the catalogue filename, since BBS-PC!
  372. X         *        doesn't always save out the null termination byte.
  373. X         *        Also set Online and Valid files to 0, by default.
  374. X         *        These will be updated by CHECKFILES.
  375. X         */
  376. X
  377. X        for (j = 0; j < bsize; j++) {
  378. X            block->cat_name[15] = '\0';
  379. X            block->online = 0;
  380. X            block->valid = 0;
  381. X            block->dirnum = 0;
  382. X            *p++ = block++;
  383. X        }
  384. X    }
  385. X    Close(infile); infile = NULL;
  386. X}
  387. X
  388. X/*
  389. X *        readconfigfile()
  390. X *        ----------------
  391. X *        This function reads in the configuration file (i.e. CONFIG.DAT),
  392. X *        if present, and initialises the directory array with the
  393. X *        directory names. If the file isn't present, readconfig is set
  394. X *        to FALSE, so that CHECKFILES won't work unless directories are
  395. X *        specified on the command line.
  396. X */
  397. Xvoid readconfigfile()
  398. X{
  399. X    int i;
  400. X    long size;
  401. X
  402. X    size = openfile(configname);
  403. X    if (size != CFGSIZE) {
  404. X        print3("BBSindex: ", configname,
  405. X            " is not a valid configuration file\n");
  406. X        Cleanup(10);
  407. X    }
  408. X    if (Read(infile, config, CFGSIZE) != CFGSIZE) {
  409. X        print3("BBSindex: Error reading from configuration file ",
  410. X                configname, "\n");
  411. X        Cleanup(10);
  412. X    }
  413. X    Close(infile);
  414. X    infile = NULL;
  415. X    for (i = 0; i < NUM_SECT; i++)
  416. X        strcpy(dirnames[i], config->ud_alt[i]);
  417. X}
  418. X
  419. X
  420. X/*
  421. X *        readscript()
  422. X *        ------------
  423. X *        This function reads in the specified script file into memory.
  424. X *        It allocates memory for the script file on the fly - this 
  425. X *        memory should be released by the caller, either through calling
  426. X *        Cleanup(), or explicitly by calling FreeMem(). If the latter,
  427. X *        remember to set script = NULL immediately afterwards.
  428. X *
  429. X *        The script pointer is set to the start of the script buffer by
  430. X *        this call, so readcommand() will start at the beginning of the file.
  431. X */
  432. X
  433. Xvoid readscript(filename)
  434. Xchar *filename;
  435. X{
  436. X    strcpy(scriptname, filename);
  437. X    scriptsize = openfile(filename);
  438. X    if (scriptsize == 0) {
  439. X        print3("BBSindex: Script file ",filename," is empty.\n");
  440. X        Cleanup(10);
  441. X    }
  442. X
  443. X    script = SafeAllocMem(scriptsize);
  444. X    if (Read(infile, script, scriptsize) != scriptsize) {
  445. X        print3("BBSindex: Error reading script file ", filename, "\n");
  446. X        Cleanup(10);
  447. X    }
  448. X    Close(infile); infile = NULL;
  449. X    scriptpos = 0;
  450. X    linenum = 1;
  451. X}
  452. X
  453. X/*
  454. X *        help()
  455. X *        ------
  456. X *        Prints out a help screen for the program.
  457. X */
  458. Xvoid help()
  459. X{
  460. X    print("BBSindex V1.0 file utility for BBS-PC! "
  461. X          "Copyright (C) Eddy Carroll 1989.\n");
  462. X    print("Usage: bbsindex {options} scriptfile ..\n\n");
  463. X    print("Possible options are:\n\n");
  464. X    print(" -c filename    Read configuration from filename (default ");
  465. X    print2(configname, ")\n");
  466. X    print(" -d filename    Read database from filename (default ");
  467. X    print2(databasename, ")\n");
  468. X    print(" -f \"string\"    Format output as string (default \""
  469. X          "%15n %w %-6x-%b{B,T}  %c\")\n");
  470. X    print(" -h             Print this help screen\n");
  471. X    print(" -n             Disable AmigaDos requesters\n");
  472. X    print(" -t             Turn on trace mode\n\n");
  473. X    print("(See documentation for a description of the script language.)\n");
  474. X    Cleanup(5);
  475. X}
  476. X
  477. X/*
  478. X *        doscript()
  479. X *        ----------
  480. X *        Reads in and executes the specified script file.
  481. X */
  482. Xvoid doscript(name)
  483. Xchar *name;
  484. X{
  485. X    readscript(name);
  486. X    execscript();
  487. X    flushout();
  488. X    FreeMem(script, scriptsize);
  489. X    script = NULL;
  490. X}
  491. X
  492. X
  493. X/*
  494. X *        Mainline
  495. X *        --------
  496. X */
  497. X
  498. Xvoid main(argc,argv)
  499. Xint argc;
  500. Xchar *argv[];
  501. X{
  502. X    struct Process *me = (struct Process *)FindTask(0L);
  503. X    requeststat = me->pr_WindowPtr;
  504. X
  505. X    /*
  506. X     *        Setup defaults for command line etc.
  507. X     */
  508. X
  509. X    errorfile    = Open("*", MODE_NEWFILE);
  510. X    outfile        = Output();
  511. X    toscreen    = IsInteractive(outfile);
  512. X    strcpy(formatstring, FORMAT);
  513. X    strcpy(databasename, UDNAME);
  514. X    strcpy(configname,   CFGNAME);
  515. X    tree[0].field = E_ALL;        /* By default, select all files */
  516. X
  517. X
  518. X    /*
  519. X     *        Open standard resources
  520. X     */
  521. X    if ((IntuitionBase = OpenLibrary("intuition.library",33)) == NULL) {
  522. X        print("BBSindex: Couldn't open intuition.library, sigh.\n");
  523. X        Cleanup(5);
  524. X    }
  525. X
  526. X    /*
  527. X     *        Allocate space for reading directory info into
  528. X     */
  529. X    fib = mymalloc(sizeof(struct FileInfoBlock));
  530. X
  531. X
  532. X    /*
  533. X     *        Check to see were we run with the name PROGSCRIPT. Only
  534. X     *        check the last part of the filename, in case we were run with
  535. X     *        a full path (like BBS:BBSCRIPT for example).
  536. X     */
  537. X    if (!stricmp(argv[0] + strlen(argv[0]) - strlen(PROGSCRIPT), PROGSCRIPT)) {
  538. X        doscript(DEFSCRIPT);
  539. X        Cleanup(0);
  540. X    }
  541. X
  542. X    /*
  543. X     *        Now parse the command line.
  544. X     */
  545. X    if (argc < 2) {
  546. X        help();
  547. X        Cleanup(0);
  548. X    }
  549. X
  550. X    while (argc > 1) {
  551. X        if (argv[1][0] == '-') {
  552. X            switch (argv[1][1]) {
  553. X
  554. X                case 'c':                    /* Set config filename */
  555. X                    argv++, argc--;
  556. X                    strcpy(configname, argv[1]);
  557. X                    break;
  558. X
  559. X                case 'd':                    /* Set database filename */
  560. X                    argv++; argc--;
  561. X                    strcpy(databasename, argv[1]);
  562. X                    break;
  563. X
  564. X                case 'f':                    /* Format string */
  565. X                    argv++; argc--;
  566. X                    strcpy(formatstring, argv[1]);
  567. X                    strcat(formatstring, "\n");
  568. X                    break;
  569. X
  570. X                case 'h':                    /* Display help */
  571. X                    help();
  572. X                    break;
  573. X
  574. X                case 'n':                    /* Disable AmigaDos requesters */
  575. X                    com_norequest();
  576. X                    break;
  577. X
  578. X                case 't':
  579. X                    tracemode = TRUE;
  580. X                    break;
  581. X
  582. X                default:
  583. X                    print3("BBSindex: Unknown option ", argv[1], "ignored\n");
  584. X                    break;
  585. X            }
  586. X        } else        /* Not a command line option, so invoke script */
  587. X            doscript(argv[1]);
  588. X        argv++, argc--;
  589. X    }
  590. X    Cleanup(0);
  591. X}
  592. X
  593. X
  594. Xvoid __fpinit(){}
  595. Xvoid __fpterm(){}
  596. Xvoid MemCleanup(){}
  597. END_OF_FILE
  598. if test 13333 -ne `wc -c <'src/bbsindex.c'`; then
  599.     echo shar: \"'src/bbsindex.c'\" unpacked with wrong size!
  600. fi
  601. # end of 'src/bbsindex.c'
  602. fi
  603. if test -f 'src/command.c' -a "${1}" != "-c" ; then 
  604.   echo shar: Will not clobber existing file \"'src/command.c'\"
  605. else
  606. echo shar: Extracting \"'src/command.c'\" \(22726 characters\)
  607. sed "s/^X//" >'src/command.c' <<'END_OF_FILE'
  608. X/*
  609. X * COMMANDS.C
  610. X *
  611. X * This module contains all the command parsing stuff used to parse the
  612. X * commands in the script file.
  613. X *
  614. X */
  615. X
  616. X#ifndef LATTICE_50
  617. X#include "system.h"
  618. X#endif
  619. X
  620. X#include "bbsindex.h"
  621. X
  622. X/*
  623. X *        The list of command names and associated functions
  624. X */
  625. X
  626. Xvoid com_append(), com_close(), com_config(), com_database(), com_echo(),
  627. X     com_exec(), com_format(), com_list(), com_macro(), com_msg(),
  628. X     com_open(), com_reset(), com_scan(), com_trace(), com_ignore();
  629. X
  630. Xstruct {
  631. X    char *name;
  632. X    void (*proc)();
  633. X} com[] = {
  634. X
  635. X    {    "APPEND",        com_append        },
  636. X    {    "CHECKFILES",    com_checkfiles    },
  637. X    {    "CLOSE",        com_close        },
  638. X    {    "CONFIG",        com_config        },
  639. X    {    "DATABASE",        com_database    },
  640. X    {    "ECHO",            com_echo        },
  641. X    {    "EXEC",            com_exec        },
  642. X    {    "FOREIGN",        com_foreign        },
  643. X    {    "FORMAT",        com_format        },
  644. X    {    "IGNORE",        com_ignore        },
  645. X    {    "LIST",            com_list        },
  646. X    {    "MACRO",        com_macro        },
  647. X    {    "MSG",            com_msg            },
  648. X    {    "NOREQUEST",    com_norequest    },
  649. X    {    "OPEN",            com_open        },
  650. X    {    "RESET",        com_reset        },
  651. X    {    "SCAN",            com_scan        },
  652. X    {    "SELECT",        com_select        },
  653. X    {    "SORT",            com_sort        },
  654. X    {    "TRACE",        com_trace        },
  655. X    {    NULL,            NULL            } 
  656. X};
  657. X
  658. Xstruct constval {
  659. X    struct constval *next;
  660. X    char *name;
  661. X    char *value;
  662. X};
  663. Xtypedef struct constval CONST;
  664. X
  665. XCONST *firstconst = NULL;        /* Pointer to first constant on list    */
  666. X
  667. Xstatic char line[MAXCOM];    /* Temporary line buffer */
  668. X
  669. X/*
  670. X *        scripterror()
  671. X *        -------------
  672. X *        Prints an error message for the current script command; the filename
  673. X *        and linenumber are automatically output, followed by the user
  674. X *        specified message. The message should be terminated with a NL,
  675. X *        unless the caller intends outputting any more info afterwards.
  676. X */
  677. Xvoid scripterror(s)
  678. Xchar *s;
  679. X{
  680. X    print3("===> ", scriptname, " (");
  681. X    print3(itoa(linenum), "): ",s);
  682. X}
  683. X
  684. X/*
  685. X *        addconst()
  686. X *        --------
  687. X *        Creates a new constant entry, and links it into the list of 
  688. X *        existing constant definitions. The name of the constant is
  689. X *        initialised appropriately. A pointer to the constant is returned.
  690. X */
  691. XCONST *addconst(name)
  692. Xchar *name;
  693. X{
  694. X    CONST *tv;
  695. X
  696. X    tv            = mymalloc(sizeof(CONST));
  697. X    tv->next    = firstconst;
  698. X    firstconst    = tv;
  699. X    tv->name    = mymalloc(strlen(name)+1);
  700. X    strcpy(tv->name, name);
  701. X    return (tv);
  702. X}
  703. X
  704. X/*
  705. X *        findconst()
  706. X *        ---------
  707. X *        Searches constant table for specified constant, and returns a
  708. X *        pointer to it if found, or NULL if not found.
  709. X */
  710. XCONST *findconst(name)
  711. Xchar *name;
  712. X{
  713. X    CONST *tv;
  714. X
  715. X    for (tv = firstconst; tv && stricmp(name, tv->name); tv = tv->next)
  716. X        ;
  717. X    return (tv);
  718. X}
  719. X
  720. X
  721. X/*
  722. X *        readcommand()
  723. X *        -------------
  724. X *        This function reads a command from the script buffer into a
  725. X *        specified command buffer. The following modifications are made
  726. X *        to the original script text:
  727. X *
  728. X *        - Anything after a # to an end of line is ignored
  729. X *        - Any line ending with \ is continued on the next line
  730. X *        - Everything outside quotes is capitalised.
  731. X *        - Any white space outside quotes gets reduced to a single space
  732. X *        - White space surrounding command lines is removed.
  733. X *        - Any commas on the line are removed, and replaced by spaces
  734. X *
  735. X *        The script command is terminated by either a semicolon or a newline.
  736. X *        The command buffer is null-terminated on return. When the end of the
  737. X *        script is reached, a 0 is returned. The maximum size of the command
  738. X *        line is specified in max - if this is exceeded, then an error message
  739. X *        is generated. Normally, the length of the command line read in is
  740. X *        returned.
  741. X *
  742. X *        For the technically minded, a mini state machine is setup, to handle
  743. X *        the different requirements.
  744. X */
  745. X
  746. X#define STATE_START        1    /* Skip over space at start of command            */
  747. X#define STATE_SPACE        2    /* Replace multiple white space by single space    */
  748. X#define STATE_COPY        3    /* Copy normal text, capitalising                */
  749. X#define STATE_IGNORE    4    /* If next char is newline, then skip it        */
  750. X#define STATE_QUOTE        5    /* Copy text up until next quote                */
  751. X#define STATE_IGQUOTE    6    /* Like STATE_IGNORE, but between quotes        */
  752. X#define STATE_COMMENT    7    /* Ignore everything until the next newline        */
  753. X#define STATE_CONST_ST    8    /* Starting to expand a constant $(..)            */
  754. X#define STATE_CONST_CP  9    /* Copying constant name, and expanding it        */
  755. X
  756. X/*
  757. X *        Retrieve next character from script buffer, updating line counter
  758. X *        and checking for end of buffer.
  759. X */
  760. X#define nextchar(ch)                                     \
  761. X    (    (ch) = script[scriptpos++],                         \
  762. X         ((ch) == CHAR_NL ? linenum++ : 0),                \
  763. X         ((scriptpos > scriptsize) ? (loop = 0) : 0)     \
  764. X    )
  765. X
  766. X/*
  767. X *        Add character to command line, checking for end of buffer and
  768. X *        aborting if the buffer is overrun.
  769. X */
  770. X#define addchar(ch)                                        \
  771. X    (    buf[pos++] = (ch),                                \
  772. X        ((pos > max) ? (scripterror("line too long\n"), Cleanup(10)) : 0)    \
  773. X    )
  774. X
  775. Xint readcommand(buf,max)
  776. Xchar *buf;
  777. Xint max;
  778. X{
  779. X    int pos = 0;
  780. X    int state = STATE_START;
  781. X    int laststate;
  782. X    int loop = 1;
  783. X    int ch;
  784. X    CONST *var;
  785. X    char varname[MAXCONST], *p;
  786. X    char openquote;
  787. X    int varpos;
  788. X
  789. X    if (scriptpos >= scriptsize)
  790. X        return 0;
  791. X
  792. X    nextchar(ch);
  793. X    while (loop) {
  794. X        switch (state) {
  795. X
  796. X            case STATE_START:
  797. X                switch (ch) {
  798. X
  799. X                    case CHAR_SPACE:
  800. X                    case CHAR_TAB:
  801. X                    case CHAR_SEMI:
  802. X                    case CHAR_NL:
  803. X                    case CHAR_COMMA:
  804. X                        nextchar(ch);
  805. X                        break;
  806. X
  807. X                    case CHAR_HASH:
  808. X                        state = STATE_COMMENT;
  809. X                        break;
  810. X
  811. X                    default:
  812. X                        state = STATE_COPY;
  813. X                        break;
  814. X                }
  815. X                break;
  816. X
  817. X            case STATE_SPACE:
  818. X                switch (ch) {
  819. X
  820. X                    case CHAR_SPACE:
  821. X                    case CHAR_TAB:
  822. X                    case CHAR_COMMA:
  823. X                        nextchar(ch);
  824. X                        break;
  825. X
  826. X                    case CHAR_NL:
  827. X                        linenum--;
  828. X                        /* Drop through */
  829. X                    case CHAR_SEMI:
  830. X                        scriptpos--;
  831. X                        loop = 0;
  832. X                        break;
  833. X
  834. X                    default:
  835. X                        addchar(CHAR_SPACE);
  836. X                        state = STATE_COPY;
  837. X                        break;
  838. X                }
  839. X                break;
  840. X
  841. X            case STATE_COPY:
  842. X                switch (ch) {
  843. X
  844. X                    /*
  845. X                     * If we get a # after a command, then we immediately
  846. X                     * stop, so that the comment will get eaten the NEXT
  847. X                     * time we call readcommand(). If we went into
  848. X                     * STATE_COMMENT, then the following command would end
  849. X                     * up getting read into the buffer as well.
  850. X                     *
  851. X                     */
  852. X                    case CHAR_HASH:
  853. X                        scriptpos--;
  854. X                        loop = 0;
  855. X                        break;
  856. X
  857. X                    case CHAR_QUOTE:
  858. X                    case CHAR_QUOTES:
  859. X                        addchar(ch);
  860. X                        openquote = ch;
  861. X                        state = STATE_QUOTE;
  862. X                        nextchar(ch);
  863. X                        break;
  864. X
  865. X                    case CHAR_DOLLAR:
  866. X                        laststate = state;    /* Save return state */
  867. X                        state = STATE_CONST_ST;
  868. X                        nextchar(ch);
  869. X                        break;
  870. X
  871. X                    case CHAR_NL:
  872. X                        linenum--;
  873. X                        /* Drop through */
  874. X
  875. X                    case CHAR_SEMI:
  876. X                        loop = 0;
  877. X                        scriptpos--;
  878. X                        break;
  879. X
  880. X                    case CHAR_ESC:
  881. X                        nextchar(ch);
  882. X                        state = STATE_IGNORE;
  883. X                        break;
  884. X
  885. X                    case CHAR_SPACE:
  886. X                    case CHAR_TAB:
  887. X                    case CHAR_COMMA:
  888. X                        state = STATE_SPACE;
  889. X                        break;
  890. X
  891. X                    default:
  892. X                        addchar(toupper(ch));
  893. X                        nextchar(ch);
  894. X                        break;
  895. X                }
  896. X                break;
  897. X
  898. X            case STATE_IGNORE:
  899. X                if (ch == CHAR_NL)
  900. X                    ch = CHAR_SPACE;
  901. X                addchar(ch);
  902. X                nextchar(ch);
  903. X                state = STATE_COPY;
  904. X                break;
  905. X
  906. X            case STATE_QUOTE:
  907. X                switch (ch) {
  908. X
  909. X                    case CHAR_NL:
  910. X                        scriptpos--;
  911. X                        linenum--;
  912. X                        loop = 0;
  913. X                        break;
  914. X
  915. X                    case CHAR_ESC:
  916. X                        state = STATE_IGQUOTE;
  917. X                        nextchar(ch);
  918. X                        break;
  919. X
  920. X                    case CHAR_DOLLAR:
  921. X                        laststate = state;
  922. X                        state = STATE_CONST_ST;
  923. X                        nextchar(ch);
  924. X                        break;
  925. X
  926. X                    case CHAR_QUOTE:
  927. X                    case CHAR_QUOTES:
  928. X                        if (openquote == ch)
  929. X                            state = STATE_COPY;
  930. X                        /* Deliberate fall through to next switch */
  931. X
  932. X                    default:
  933. X                        addchar(ch);
  934. X                        nextchar(ch);
  935. X                        break;
  936. X                }
  937. X                break;
  938. X
  939. X            case STATE_IGQUOTE:
  940. X                if (ch != CHAR_NL) {
  941. X                    addchar(CHAR_ESC);
  942. X                    addchar(ch);
  943. X                }
  944. X                nextchar(ch);
  945. X                state = STATE_QUOTE;
  946. X                break;
  947. X
  948. X            case STATE_COMMENT:
  949. X                if (ch == CHAR_NL)
  950. X                    state = STATE_START;
  951. X                nextchar(ch);
  952. X                break;
  953. X
  954. X            case STATE_CONST_ST:
  955. X                if (ch != '(') { /* If not a constant usage, copy unchanged */
  956. X                    addchar(CHAR_DOLLAR);
  957. X                    state = laststate;
  958. X                    break;
  959. X                }
  960. X                varpos = 0;
  961. X                nextchar(ch);
  962. X                state = STATE_CONST_CP;
  963. X                break;
  964. X
  965. X            case STATE_CONST_CP:
  966. X                if (ch == ')') {
  967. X                    /*
  968. X                     *        Variable name has been fully entered, so now
  969. X                     *        expand it.
  970. X                     */
  971. X                    varname[varpos] = CHAR_NULL;
  972. X                    var = findconst(varname);
  973. X                    if (!var) {
  974. X                        scripterror("unknown constant ");
  975. X                        print2(varname, ".\n");
  976. X                        Cleanup(10);
  977. X                    }
  978. X                    for (p = var->value; *p; p++)
  979. X                        addchar(*p);
  980. X                    state = laststate;
  981. X                } else {
  982. X                    /*
  983. X                     *        Else still gathering constant name, so keep
  984. X                     *        copying into array.
  985. X                     */
  986. X                    if (varpos >= MAXCONST) {
  987. X                        scripterror("constant name too long.\n");
  988. X                        Cleanup(10);
  989. X                    }
  990. X                    varname[varpos++] = ch;
  991. X                }
  992. X                nextchar(ch);
  993. X                break;
  994. X        }
  995. X    }
  996. X    addchar(CHAR_NULL);    /* Null terminate command string */
  997. X    compos = 0;
  998. X    comlen = strlen(buf);
  999. X    return (comlen);
  1000. X}
  1001. X
  1002. X/*
  1003. X *        findmacro()
  1004. X *        -----------
  1005. X *        Searches the macro table for the specified macro. If found, returns
  1006. X *        pointer to the macro definition, else returns -1.
  1007. X */
  1008. Xint findmacro(name)
  1009. Xchar *name;
  1010. X{
  1011. X    int i;
  1012. X
  1013. X    for (i = 0; i < nummacros; i++)
  1014. X        if (!strcmp(macros[i]->name, name))
  1015. X            return (i);
  1016. X    return (-1);
  1017. X}
  1018. X
  1019. X/*
  1020. X *        getstring()
  1021. X *        -----------
  1022. X *        This function scans the command buffer starting at position
  1023. X *        'compos', and returns a pointer to the next identifier/string in
  1024. X *        the buffer. The string is null-terminated, and if it was enclosed
  1025. X *        in quotes, these are removed. compos is left pointing to just after
  1026. X *        the string.
  1027. X */
  1028. Xchar *getstring()
  1029. X{
  1030. X    char *s = combuf + compos;
  1031. X    char *p, openquote;
  1032. X
  1033. X    if (compos > comlen) {
  1034. X        scripterror("missing parameter\n");
  1035. X        Cleanup(10);
  1036. X    }
  1037. X    if (*s == CHAR_SPACE)
  1038. X        s++;
  1039. X
  1040. X    if (*s == CHAR_QUOTE || *s == CHAR_QUOTES) {
  1041. X        openquote = *s++;
  1042. X        for (p = s; *p && *p != openquote; p++) {
  1043. X            if (*p == CHAR_ESC)
  1044. X                p++;    /* Skip over possible escaped quotes */
  1045. X        }
  1046. X        *p = CHAR_NULL;
  1047. X        compos = (p - combuf) + 1;
  1048. X        return (s);
  1049. X    }
  1050. X
  1051. X    for (p = s; *p && *p != CHAR_SPACE; p++)
  1052. X        ;
  1053. X    *p = CHAR_NULL;
  1054. X    compos = (p - combuf) + 1;
  1055. X    return (s);
  1056. X}
  1057. X
  1058. X
  1059. X/*
  1060. X *        execline()
  1061. X *        ----------
  1062. X *        This function executes the current line in the command buffer,
  1063. X *        handling macro expansion as necessary.
  1064. X */
  1065. Xvoid execline()
  1066. X{
  1067. X    char *cmd;
  1068. X    char *p, *s;
  1069. X    char *equals;
  1070. X    MACRO *curmac;
  1071. X    CONST *var;
  1072. X    int macronum;
  1073. X    char *ps[10];
  1074. X    PARAM *par;
  1075. X    int length;
  1076. X    int i;
  1077. X
  1078. X    chkabort();
  1079. X    if (tracemode) {
  1080. X        if (nestlevel > 0) {
  1081. X            int i;
  1082. X            for (i = 0; i < nestlevel; i++)
  1083. X                print("    ");
  1084. X            print2(combuf, "\n");
  1085. X        } else {
  1086. X            print3(itoa(linenum), ":", scriptname);
  1087. X            print3(": ", combuf, "\n");
  1088. X        }
  1089. X    }
  1090. X    cmd = getstring();
  1091. X    for (i = 0; com[i].name && strcmp(cmd, com[i].name); i++)
  1092. X        ;
  1093. X    if (com[i].name) {
  1094. X        /*
  1095. X         *        A valid command was found, so execute it
  1096. X         */
  1097. X        com[i].proc();
  1098. X    } else if ((macronum = findmacro(cmd)) == -1) {
  1099. X        /*
  1100. X         *        Not a macro -- is it a constant definition?
  1101. X         */
  1102. X        if (compos < comlen) {
  1103. X            /*
  1104. X             *        There's a parameter after it; check is it an equals sign
  1105. X             */
  1106. X            equals = getstring();
  1107. X            if (equals[0] != CHAR_EQUALS || equals[1] != CHAR_NULL) {
  1108. X                /*
  1109. X                 *        Wasn't an equals, so probably just a wrong command
  1110. X                 *        with some superfluous parameters.
  1111. X                 */
  1112. X                scripterror("unknown command ");
  1113. X                print2(cmd, "\n");
  1114. X                Cleanup(10);
  1115. X            }
  1116. X            /*
  1117. X             *        Okay, it's a constant definition. If it's already been
  1118. X             *        defined, print a warning but carry on anyway.
  1119. X             */
  1120. X            var = findconst(cmd);
  1121. X            if (var) {
  1122. X                scripterror("constant ");
  1123. X                print2(cmd, " redefined.\n");
  1124. X            } else
  1125. X                var = addconst(cmd);
  1126. X            /*
  1127. X             *        Now see was a value specified for the constant.
  1128. X             *        If it was, copy it into constant, else just 
  1129. X             *        setup a null definition.
  1130. X             */
  1131. X            if (compos < comlen) {
  1132. X                /*
  1133. X                 *        Copy user's definition
  1134. X                 */
  1135. X                cmd = getstring();
  1136. X                if (strlen(cmd) >= MAXCONST) {
  1137. X                    scripterror("constant name too long.\n");
  1138. X                    Cleanup(10);
  1139. X                }
  1140. X                var->value = mymalloc(strlen(cmd)+1);
  1141. X                strcpy(var->value, cmd);
  1142. X            } else {
  1143. X                /*
  1144. X                 *        Setup null constant definition
  1145. X                 */
  1146. X                var->value = mymalloc(1);
  1147. X                strcpy(var->value, "");
  1148. X            }
  1149. X        } else {
  1150. X            /*
  1151. X             *        Not a command, not a macro, not a constant definition.
  1152. X             *        Therefore, must be an error.
  1153. X             */
  1154. X            scripterror("unknown command ");
  1155. X            print2(cmd, "\n");
  1156. X            Cleanup(10);
  1157. X        }
  1158. X    } else {
  1159. X        /*
  1160. X         *        It's a macro. Save a copy of the macro parameters into
  1161. X         *        $0 to $9, and then execute each line of the macro definition,
  1162. X         *        expanding parameter usages as necessary.
  1163. X         */
  1164. X        curmac = macros[macronum];
  1165. X        length = 0;
  1166. X        if (nestlevel >= MAXNEST) {
  1167. X            scripterror("macros can only be nested ");
  1168. X            print2(MAXNEST, " deep\n");
  1169. X            Cleanup(10);
  1170. X        }
  1171. X        par = SafeAllocMem(PARAMSIZE + comlen + 1);
  1172. X        params[nestlevel++] = par;
  1173. X        par->size = comlen + 1;
  1174. X
  1175. X        /*
  1176. X         *        Now initialise parameters $0 to $9
  1177. X         */
  1178. X        ps[0] = par->params;
  1179. X        for (i = 1; i < 10; i++) {
  1180. X            if (compos < comlen)
  1181. X                /* Save parameter for later */
  1182. X                ps[i] = par->params + (getstring() - combuf);
  1183. X            else
  1184. X                ps[i] = NULL;
  1185. X        }
  1186. X        memcpy(par->params, combuf, comlen+1);
  1187. X
  1188. X        /*
  1189. X         *        Now recursively call execline() with command buffer setup
  1190. X         *        to be the current macro buffer line.
  1191. X         */
  1192. X        length = 0;
  1193. X        s = curmac->text;
  1194. X        for (length = 0; length < curmac->size;
  1195. X                        length += strlen(s) + 1, s = curmac->text + length) {
  1196. X            /* Copy macro line into buffer, doing expansion */
  1197. X            comlen = 0;
  1198. X            for (p = s; *p; p++) {
  1199. X                if (*p == '$') {
  1200. X                    p++;
  1201. X                    if (!*p) {
  1202. X                        scripterror("'$' must be followed by something\n");
  1203. X                        Cleanup(10);
  1204. X                    }
  1205. X                    if (isdigit(*p)) {
  1206. X                        int num = *p - '0';
  1207. X
  1208. X                        if (ps[num]) {
  1209. X                            /* Copy macro definition */
  1210. X                            if ((strlen(ps[num]) + comlen) >= MAXCOM - 10)
  1211. X                                goto endcommand;    /* Eek! my first C Goto! */
  1212. X                            strcpy(combuf+comlen, ps[num]);
  1213. X                            comlen += strlen(ps[num]);
  1214. X                        }
  1215. X                        continue;
  1216. X
  1217. X                    }
  1218. X                }
  1219. X                combuf[comlen++] = *p;
  1220. X            }
  1221. Xendcommand:
  1222. X            combuf[comlen] = CHAR_NULL;
  1223. X            compos = 0;
  1224. X            execline();
  1225. X        }
  1226. X        /*
  1227. X         *        Now free memory allocated for macro parameters
  1228. X         */
  1229. X        FreeMem(par, PARAMSIZE + par->size);
  1230. X        nestlevel--;
  1231. X    }
  1232. X}
  1233. X
  1234. X
  1235. X/*
  1236. X *        execscript()
  1237. X *        ------------
  1238. X *        This function goes through all the commands in the script file,
  1239. X *        executing them one by one. Each command is parsed, and then
  1240. X *        the appropriate function called. Any errors that occur cause the
  1241. X *        script to be aborted, so if this call returns, the script was
  1242. X *        successfully executed.
  1243. X */
  1244. Xvoid execscript()
  1245. X{
  1246. X    while (readcommand(combuf, MAXCOM))
  1247. X        execline();
  1248. X}
  1249. X
  1250. X/*
  1251. X *        ====> Now the actual commands follow <====
  1252. X */
  1253. X
  1254. X/*
  1255. X *        com_append()
  1256. X *        ------------
  1257. X *        This file opens the specified file for appending. If there was
  1258. X *        already a file open, it is closed.
  1259. X */
  1260. X
  1261. Xvoid com_append()
  1262. X{
  1263. X    char *filename = getstring();
  1264. X    BPTR lock;
  1265. X
  1266. X    com_close();
  1267. X
  1268. X    if (lock = Lock(filename, ACCESS_READ)) {
  1269. X        UnLock(lock);
  1270. X        outfile = Open(filename, MODE_OLDFILE);
  1271. X    } else
  1272. X        outfile = Open(filename, MODE_NEWFILE);
  1273. X
  1274. X    if (!outfile) {
  1275. X        scripterror("error appending to file ");
  1276. X        print2(filename, "\n");
  1277. X        Cleanup(10);
  1278. X    }
  1279. X    Seek(outfile, 0, OFFSET_END);
  1280. X    toscreen = IsInteractive(outfile);
  1281. X}
  1282. X
  1283. X/*
  1284. X *        com_open()
  1285. X *        ----------
  1286. X *        This command opens a file for output. The output of the ECHO,
  1287. X *        FOREIGN and LIST commands goes to this file. If an output file
  1288. X *        was already open, it is closed first.
  1289. X */
  1290. Xvoid com_open()
  1291. X{
  1292. X    char *filename = getstring();
  1293. X
  1294. X    com_close();
  1295. X    outfile = Open(filename, MODE_NEWFILE);
  1296. X    if (!outfile) {
  1297. X        scripterror("error opening file ");
  1298. X        print2(filename, "\n");
  1299. X        Cleanup(10);
  1300. X    }
  1301. X    toscreen = IsInteractive(outfile);
  1302. X}
  1303. X
  1304. X/*
  1305. X *        com_close()
  1306. X *        -----------
  1307. X *        Closes the current output file, if any. All future output goes to
  1308. X *        stdout.
  1309. X */
  1310. Xvoid com_close()
  1311. X{
  1312. X    if (outfile != Output()) {
  1313. X        flushout();
  1314. X        Close(outfile);
  1315. X        outfile = Output();
  1316. X        toscreen = IsInteractive(outfile);
  1317. X    }
  1318. X}
  1319. X
  1320. X/*
  1321. X *        com_config()
  1322. X *        ------------
  1323. X *        This command sets the name of the configuration file used by
  1324. X *        CHECKFILES to get the names of the directories to scan from.
  1325. X */
  1326. Xvoid com_config()
  1327. X{
  1328. X    strcpy(configname, getstring());
  1329. X}
  1330. X
  1331. X/*
  1332. X *        com_database()
  1333. X *        --------------
  1334. X *        This command sets the name of the database file read in
  1335. X *        which contains the details of all the file headers.
  1336. X */
  1337. Xvoid com_database()
  1338. X{
  1339. X    strcpy(databasename, getstring());
  1340. X}
  1341. X
  1342. X/*
  1343. X *        com_norequest()
  1344. X *        ---------------
  1345. X *        This command stops AmigaDos from putting up requesters when an
  1346. X *        error occurs, or a volume isn't mounted. 
  1347. X */
  1348. Xvoid com_norequest()
  1349. X{
  1350. X    struct Process *me = (struct Process *)FindTask(0L);
  1351. X    me->pr_WindowPtr = (APTR)-1L;
  1352. X}
  1353. X
  1354. X/*
  1355. X *        com_echo()
  1356. X *        ----------
  1357. X *        This command echoes the specified string to the output file.
  1358. X *        A number of meta characters may be present in the string to be
  1359. X *        output. See the documentation for more info. If any other parameter
  1360. X *        is present on the line after the string, no newline is added
  1361. X *        to the output, otherwise a newline is added automatically.
  1362. X *
  1363. X */
  1364. Xvoid com_echo()
  1365. X{
  1366. X    strcpy(line, getstring());
  1367. X    if (compos >= comlen)     /* Add NL if no second parameter */
  1368. X        strcat(line, "\n");
  1369. X    putstring(echoformat(out, MAXOUT, line));
  1370. X}
  1371. X
  1372. X/*
  1373. X *        com_exec()
  1374. X *        ----------
  1375. X *        This command executes the indicated AmigaDOS command. The output
  1376. X *        from the command goes into the current output file, if any.
  1377. X */
  1378. Xvoid com_exec()
  1379. X{
  1380. X    flushout();
  1381. X    Execute(getstring(), NULL, outfile);
  1382. X}
  1383. X
  1384. X/*
  1385. X *        com_format()
  1386. X *        ------------
  1387. X *        This command sets up the format string used by LIST and FOREIGN.
  1388. X *        For details of what the format may contain, see the documentation.
  1389. X */
  1390. Xvoid com_format()
  1391. X{
  1392. X    strcpy(formatstring, getstring());
  1393. X    if (compos >= comlen)                /* No second parameter */
  1394. X        strcat(formatstring, "\n");
  1395. X}
  1396. X
  1397. X/*
  1398. X *        com_list()
  1399. X *        ----------
  1400. X *        This command is the biggy! It scans through the entire file
  1401. X *        database, selecting records as specified with SELECT, and printing
  1402. X *        them using the order specified with FORMAT. The order of printing
  1403. X *        is as specified with SORT.
  1404. X */
  1405. Xvoid com_list()
  1406. X{
  1407. X    int i;
  1408. X
  1409. X    CHECKDATABASE();
  1410. X    curbytes = 0;
  1411. X    curfiles = 0;
  1412. X    for (i = 0; i < numrecs; i++) {
  1413. X        if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
  1414. X            format(out, MAXOUT, formatstring, ptrblock[i], checkfiles);
  1415. X            putstring(out);
  1416. X            curfiles++;
  1417. X            curbytes += ptrblock[i]->length;
  1418. X        }
  1419. X        chkabort();
  1420. X    }
  1421. X    totalbytes += curbytes;
  1422. X    totalfiles += curfiles;
  1423. X}
  1424. X
  1425. X/*
  1426. X *        com_msg()
  1427. X *        ---------
  1428. X *        This command is identical to the ECHO command, except that
  1429. X *        the output goes to the screen (i.e. stderr) instead of the
  1430. X *        current output file. It is intended for printing informational
  1431. X *        messages to let the user know of the program's progress, while
  1432. X *        processing a large script.
  1433. X */
  1434. Xvoid com_msg()
  1435. X{
  1436. X    strcpy(line, getstring());
  1437. X    if (compos >= comlen)     /* Add NL if no second parameter */
  1438. X        strcat(line, "\n");
  1439. X    print(echoformat(out, MAXOUT, line));
  1440. X}
  1441. X
  1442. X/*
  1443. X *        com_reset()
  1444. X *        -----------
  1445. X *        This command resets the running totals which are maintained, 
  1446. X *        which give the total number of bytes and total number of files
  1447. X *        output so far.
  1448. X */
  1449. Xvoid com_reset()
  1450. X{
  1451. X    totalfiles = 0;
  1452. X    totalbytes = 0;
  1453. X}
  1454. X
  1455. X/*
  1456. X *        com_scan()
  1457. X *        ----------
  1458. X *        This command is identical to com_list(), except that no output is
  1459. X *        produced. It is provided to allow counters to be updated, without
  1460. X *        producing output, so that for example, the total number of files
  1461. X *        in a listing can be output before the files themselves.
  1462. X */
  1463. X
  1464. Xvoid com_scan()
  1465. X{
  1466. X    int i;
  1467. X
  1468. X    CHECKDATABASE();
  1469. X    curbytes = 0;
  1470. X    curfiles = 0;
  1471. X    for (i = 0; i < numrecs; i++) {
  1472. X        if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
  1473. X            curfiles++;
  1474. X            curbytes += ptrblock[i]->length;
  1475. X        }
  1476. X        chkabort();
  1477. X    }
  1478. X    totalbytes += curbytes;
  1479. X    totalfiles += curfiles;
  1480. X}
  1481. X
  1482. X/*
  1483. X *        com_macro()
  1484. X *        -----------
  1485. X *        This command lets you define a macro. The syntax is:
  1486. X *
  1487. X *            MACRO name
  1488. X *                  ..
  1489. X *                commands
  1490. X *                  ..
  1491. X *            ENDM
  1492. X *
  1493. X *        The macro can contain $1 to $9, which will be substituted at
  1494. X *        run time by the appropriate parameters that were passed when
  1495. X *        the macro was invoked.
  1496. X */
  1497. Xvoid com_macro()
  1498. X{
  1499. X    static char macroname[MACROLEN];
  1500. X    int curline, curpos;    /* Saves current line # and position in script */
  1501. X    int length;                /* Length of macro script                       */
  1502. X    int macronum;
  1503. X    MACRO *curmac;
  1504. X    char *buf, *p;
  1505. X
  1506. X    if (nummacros >= MAXMACRO) {
  1507. X        scripterror("maximum of ");
  1508. X        print2(itoa(MAXMACRO), " macros allowed\n");
  1509. X        Cleanup(10);
  1510. X    }
  1511. X
  1512. X    curline = linenum;
  1513. X    curpos = scriptpos;
  1514. X    
  1515. X    p = getstring();
  1516. X    strncpy(macroname, p, MACROLEN-1);
  1517. X    macroname[MACROLEN-1] = CHAR_NULL;
  1518. X
  1519. X    /*
  1520. X     *        First of all, find out how much space the macro occupies
  1521. X     */
  1522. X    length = 0;
  1523. X    while (readcommand(combuf, MAXCOM) && strcmp(combuf, "ENDM"))
  1524. X        length += comlen + 1;    /* The extra 1 is for the terminating \0 */
  1525. X
  1526. X    /*
  1527. X     *        Now, restore linenumber and script position so we can read
  1528. X     *        in macro for real the second time.
  1529. X     */
  1530. X    linenum = curline;
  1531. X    if (scriptpos >= scriptsize) {
  1532. X        scripterror("missing ENDM in macro definition\n");
  1533. X        Cleanup(10);
  1534. X    }
  1535. X
  1536. X    scriptpos = curpos;
  1537. X
  1538. X    if ((macronum = findmacro(macroname)) == -1)         /* New macro */
  1539. X        macronum = nummacros++;
  1540. X    else
  1541. X        /* Macro redefinition, so release earlier definition */
  1542. X        FreeMem(macros[macronum], macros[macronum]->size + MACROSIZE);
  1543. X
  1544. X    curmac = SafeAllocMem(MACROSIZE + length);
  1545. X    macros[macronum] = curmac;
  1546. X    strcpy(curmac->name, macroname);
  1547. X    curmac->size = length;
  1548. X    buf = curmac->text;
  1549. X
  1550. X    /*
  1551. X     *        Initialised macro definition, now copy macro text from script
  1552. X     *        file into macro buffer.
  1553. X     */
  1554. X    while (readcommand(combuf, MAXCOM) && strcmp("ENDM", combuf)) {
  1555. X        strcpy(buf, combuf);
  1556. X        buf += comlen + 1;
  1557. X    }
  1558. X}
  1559. X
  1560. X/*
  1561. X *        com_trace()
  1562. X *        -----------
  1563. X *        Turns on or off trace mode. When trace mode is on, every line that
  1564. X *        is executed is displayed first. This is handy when executing
  1565. X *        macros, to see exactly what is happening. Note that the -t option
  1566. X *        on the command line will enable tracing for the whole file,
  1567. X *        but if TRACE ON or OFF is encountered, this overrides -t.
  1568. X */
  1569. Xvoid com_trace()
  1570. X{
  1571. X    char *opt = getstring();
  1572. X
  1573. X    if (!strcmp(opt, "ON"))
  1574. X        tracemode = TRUE;
  1575. X    else if (!strcmp(opt, "OFF"))
  1576. X        tracemode = FALSE;
  1577. X    else {
  1578. X        scripterror("TRACE ON or TRACE OFF expected\n");
  1579. X        Cleanup(10);
  1580. X    }
  1581. X}
  1582. X
  1583. X/*
  1584. X *        com_ignore()
  1585. X *        ------------
  1586. X *        This command takes a list of filenames. Each filename listed here
  1587. X *        is remembered, and when CHECKFILES is done, any filename listed
  1588. X *        here will be marked as valid, regardless of whether it really IS
  1589. X *        valid or not. This is useful if you have some files online which
  1590. X *        are updated by external programs (such as, for example, the output
  1591. X *        from BBSINFO), and hence have "wandering" file sizes.
  1592. X */
  1593. Xvoid com_ignore()
  1594. X{
  1595. X    IGNORE *ig;
  1596. X    char *p;
  1597. X
  1598. X    while (compos < comlen) {
  1599. X        ig = mymalloc(sizeof(IGNORE));
  1600. X        p = getstring();
  1601. X        if (strlen(p) >= CAT_LEN) {
  1602. X            scripterror("filename too long.\n");
  1603. X            Cleanup(10);
  1604. X        }
  1605. X        strcpy(ig->name, p);
  1606. X        ig->next = firstignore;
  1607. X        firstignore = ig;
  1608. X    }
  1609. X}
  1610. END_OF_FILE
  1611. if test 22726 -ne `wc -c <'src/command.c'`; then
  1612.     echo shar: \"'src/command.c'\" unpacked with wrong size!
  1613. fi
  1614. # end of 'src/command.c'
  1615. fi
  1616. if test -f 'src/format.c' -a "${1}" != "-c" ; then 
  1617.   echo shar: Will not clobber existing file \"'src/format.c'\"
  1618. else
  1619. echo shar: Extracting \"'src/format.c'\" \(18069 characters\)
  1620. sed "s/^X//" >'src/format.c' <<'END_OF_FILE'
  1621. X/*
  1622. X *    FORMAT.C
  1623. X *
  1624. X *    This is a general purpose format routine for BBSINDEX. It takes
  1625. X *    as parameters a format string and a pointer to a file header and creates
  1626. X *    a string, based on the format string, but containing information from
  1627. X *    the file header.
  1628. X *
  1629. X *  The format string is similar to a printf format string. The following
  1630. X *  special sequences are translated into values associated with the
  1631. X *    fileheader.
  1632. X *
  1633. X *        %a        Number of accesses
  1634. X *        %b{}    File type {Binary,Text}
  1635. X *        %c        Comment
  1636. X *        %d        Disk filename
  1637. X *        %f        Full name of disk file
  1638. X *        %i{}    File status {Online,Offline}
  1639. X *        %k        File size in K
  1640. X *        %l{}    File origin {Local,Remote}
  1641. X *        %n        Filename
  1642. X *        %o        Uploader's name (owner)
  1643. X *        %p        Path name of disk file
  1644. X *        %r        Directory number
  1645. X *        %s        Section number or letter
  1646. X *        %ux        Output x n times, where n is length of last {sub}format cmd
  1647. X *        %v{}    File contents {Valid,Invalid}
  1648. X *        %w        Upload date (when)
  1649. X *        %x        File size
  1650. X *        %y        Disk directory number
  1651. X *        %{...}    Format substring in { }
  1652. X *    
  1653. X *    The following escape sequences are also recognised:
  1654. X *    
  1655. X *        \n        - End of line
  1656. X *        \e        - Escape
  1657. X *        \E        - CSI (0x9b)
  1658. X *        \t        - Tab
  1659. X *        \nnn    - Octal number with the appropriate value
  1660. X *    
  1661. X *    Any other characters after a \ are treated as normal. This can be used
  1662. X *    to escape certain characters that have special meaning, such as \ itself,
  1663. X *    %, and sometimes {, }, and comma.
  1664. X *    
  1665. X *    With the % flags, additional characters may appear between the % and the
  1666. X *    value. Using %c as an example, %20c means make the comment 20 characters
  1667. X *    wide, padding with spaces as necessary. The comment is aligned to the
  1668. X *    left. %-20c is similar, but aligns the comment to the right. This can
  1669. X *    be useful for padding. Items followed by {} are boolean flags, which
  1670. X *    can be either true or false. Inside the {} are two items, seperated by
  1671. X *    a comma. If the flag is true, the first value is used, else the second
  1672. X *    value. The expressions inside {} may not include %.
  1673. X *
  1674. X *    The exception to this is %m, which can contain a complete format
  1675. X *    string within the {}. In this case, the substring is formatted as
  1676. X *    normal, and then the resulting string is formatted according to
  1677. X *    the parameters between the % and m. For example %10m{(%s,%d)} formats
  1678. X *    the section and directory number in brackets, while ensuring that
  1679. X *    the total field width is 10 characters.
  1680. X *
  1681. X *    n.b. A file is said to be valid if its actual filesize is equivalent
  1682. X *  to the filesize in the file catalogue.
  1683. X *
  1684. X */
  1685. X
  1686. X#ifndef LATTICE_50
  1687. X#include "system.h"
  1688. X#endif
  1689. X
  1690. X#include "bbsindex.h"
  1691. X
  1692. X#define LEFT    0        /* Left alignment                */
  1693. X#define RIGHT    1        /* Right alignment                */
  1694. X#define MAXPATH    256        /* Maximum length of disk path    */
  1695. X
  1696. X#define MIN(a,b) ((a) > (b) ? (b) : (a))
  1697. X
  1698. X#define    LEFTBR    '{'        /* Left bracket                    */
  1699. X#define RIGHTBR    '}'        /* Right bracket                */
  1700. X#define BACKSLASH '\\'    /* Backslash                    */
  1701. X
  1702. Xstatic int len;            /* Length of format option            */
  1703. Xstatic int align;        /* Text alignment - LEFT or RIGHT    */
  1704. Xstatic int opos;        /* Position in output string        */
  1705. Xstatic int omaxlen;        /* Maximum length of output string    */
  1706. X
  1707. Xstatic char buf[256];
  1708. X
  1709. Xstatic char *sectnums[] = {
  1710. X    "0", "1", "2", "3", "4", "5", "6", "7",
  1711. X    "8", "9", "A", "B", "C", "D", "E", "F"
  1712. X};
  1713. X
  1714. Xstatic char *days[] = {
  1715. X    "Sunday",    "Monday",    "Tuesday",    "Wednesday",
  1716. X    "Thursday",    "Friday",    "Saturday"
  1717. X};
  1718. X
  1719. X/*
  1720. X *        doescape()
  1721. X *        ----------
  1722. X *        This function parses the escape sequence passed as a string,
  1723. X *        and stores the character it corresponds to in the output character.
  1724. X *        it returns the number of characters taken up by the escape sequence.
  1725. X */
  1726. Xint doescape(out, seq)
  1727. Xunsigned char *out;
  1728. Xunsigned char *seq;
  1729. X{
  1730. X    int num = 0;
  1731. X    char *p;
  1732. X
  1733. X    if (*seq >= '0' && *seq <= '7') {
  1734. X        p = seq;
  1735. X        for (p = seq; *p >= '0' && *p <= '7'; p++)
  1736. X            num = (num * 8) + (*seq - '0');
  1737. X        *out = num;
  1738. X        return (p-seq);
  1739. X    }
  1740. X    switch (*seq) {
  1741. X
  1742. X        case 't':
  1743. X            *out = '\t';
  1744. X            break;
  1745. X
  1746. X        case 'n':
  1747. X            *out = '\n';
  1748. X            break;
  1749. X
  1750. X        case 'e':
  1751. X            *out = '\033';
  1752. X            break;
  1753. X
  1754. X        case 'E':
  1755. X            *out = '\233';
  1756. X            break;
  1757. X
  1758. X        default:
  1759. X            *out = *seq;
  1760. X            break;
  1761. X    }
  1762. X    return (1);
  1763. X}
  1764. X
  1765. X
  1766. X/*
  1767. X *        makedate()
  1768. X *        ----------
  1769. X *        This function takes the passed day, month and year and returns
  1770. X *        a pointer to a string containing dd-mmm-yy (e.g. 23-Mar-89).
  1771. X */
  1772. X
  1773. X#define ITOA(c1,c2,x) ((c1) = ((x)/10 + '0'),(c2) = (((x) % 10) + '0'))
  1774. X
  1775. Xchar *makedate(day, month, year)
  1776. Xint day, month, year;
  1777. X{
  1778. X    static char buf[10];
  1779. X
  1780. X    ITOA(buf[0], buf[1], day);
  1781. X    buf[3] = months[month][0];
  1782. X    buf[4] = months[month][1];
  1783. X    buf[5] = months[month][2];
  1784. X    ITOA(buf[7], buf[8], year);
  1785. X    buf[2] = '-';
  1786. X    buf[6] = '-';
  1787. X    buf[9] = CHAR_NULL;
  1788. X    return (buf);
  1789. X}
  1790. X
  1791. X/*
  1792. X *        maketime()
  1793. X *        ----------
  1794. X *        Returns a pointer to a string containing the specified time, in
  1795. X *        the form HH:MM:SS.
  1796. X */
  1797. Xchar *maketime(secs, mins, hours)
  1798. Xint secs, mins, hours;
  1799. X{
  1800. X    static char buf[9];
  1801. X    ITOA(buf[0], buf[1], hours);
  1802. X    ITOA(buf[3], buf[4], mins);
  1803. X    ITOA(buf[6], buf[7], secs);
  1804. X    buf[2] = ':';
  1805. X    buf[5] = ':';
  1806. X    buf[8] = CHAR_NULL;
  1807. X    return (buf);
  1808. X}
  1809. X
  1810. X/*
  1811. X *        itoa()
  1812. X *        ------
  1813. X *        This function returns a pointer to a string containing the ascii
  1814. X *        representation of the number. The pointer is valid until the next
  1815. X *        time itoa() is called. Negative numbers are not handled.
  1816. X */
  1817. Xchar *itoa(n)
  1818. Xint n;
  1819. X{
  1820. X    static char buf[20];
  1821. X    int i = 18;
  1822. X
  1823. X    buf[19] = CHAR_NULL;
  1824. X    if (n == 0) {
  1825. X        buf[18] = '0';
  1826. X        return (buf + 18);
  1827. X    } else {
  1828. X        for ( ; n && (i > 0); i--) {
  1829. X            buf[i] = (n % 10) + '0';
  1830. X            n = n / 10;
  1831. X        }
  1832. X    }
  1833. X    return (buf+i+1);
  1834. X}
  1835. X
  1836. X
  1837. X/*
  1838. X *        addstring()
  1839. X *        -----------
  1840. X *        This functions adds string 's' to string 'out' starting at position
  1841. X *        (global) opos. If (global) len == 0, then no special formatting is
  1842. X *        done. Else, the string is formatted in a field of width 'len'
  1843. X *        characters, truncating or padding with spaces as appropriate. If
  1844. X *        (global) align == LEFT, then the string is aligned to the left
  1845. X *        of the field, else to the right.
  1846. X *
  1847. X *        If opos exceeds omaxlen at any point, then no more copying is done.
  1848. X */
  1849. X
  1850. Xvoid addstring(out, s)
  1851. Xchar *out;
  1852. Xchar *s;
  1853. X{
  1854. X    int oleft = omaxlen - opos;    /* Number of chars left in output string */
  1855. X    int slen = strlen(s);
  1856. X
  1857. X    /* Note: Be VERY careful the following hasn't got changed into tabs! */
  1858. X    static char pad[] = "\
  1859. X                                                                          \
  1860. X                                                                          \
  1861. X                                                                          ";
  1862. X    /* End of space definition! There should be ~200 spaces */
  1863. X
  1864. X    if (len == 0) {                /* No special alignment needed */
  1865. X        if (oleft > 0)
  1866. X            strncpy(out+opos,s,MIN(slen,oleft));
  1867. X        opos = opos + slen;
  1868. X    } else {                    /* Do special alignment */
  1869. X        if (slen > len)
  1870. X            strncpy(out+opos, s, len);
  1871. X        else {
  1872. X            if (align == LEFT) {
  1873. X                if (oleft > 0)
  1874. X                    /* Copy string into left of field */
  1875. X                    strncpy(out+opos, s, MIN(slen,oleft));
  1876. X                oleft = oleft - slen;
  1877. X                if (oleft > 0 && len > slen)
  1878. X                    /* Copy padding in to rest of field */
  1879. X                    strncpy(out+opos+slen, pad, MIN(len-slen, oleft));
  1880. X            } else {    /* Align == RIGHT */
  1881. X                if (len > slen)
  1882. X                    /* Copy padding into left of field */
  1883. X                    strncpy(out+opos, pad, MIN(oleft, len-slen));
  1884. X                if (oleft > 0)
  1885. X                    /* Copy string into right of field */
  1886. X                    strncpy(out+opos+(len-slen), s, MIN(slen,oleft-slen));
  1887. X            }
  1888. X        }
  1889. X        opos = opos + len;
  1890. X    }
  1891. X}
  1892. X
  1893. X/*
  1894. X *        addnumber()
  1895. X *        -----------
  1896. X *        This macro adds the ascii representation of the number n
  1897. X *        to the output buffer 'out', starting at position opos (global).
  1898. X *        See addstring() for more details.
  1899. X */
  1900. X
  1901. X#define addnumber(out,n) addstring((out),itoa(n))
  1902. X
  1903. X/*
  1904. X *        format()
  1905. X *        --------
  1906. X *        This function converts the format string f and the file header into
  1907. X *        an output string. See above for valid options in the format string.
  1908. X *        A pointer to the output string is returned. Maxlen is the maximum
  1909. X *        length of the output string. Checkfiles is a boolean which is true
  1910. X *        if the dirnum, online and valid fields in the file header are valid.
  1911. X *        These are normally NOT valid, unless requested by the user.
  1912. X *
  1913. X *        The external variable dirnames is expected to exist. Dirnames is
  1914. X *        an array of disk dirctories (NOT BBS file directories).
  1915. X */
  1916. X
  1917. Xchar *format(out, maxlen, f, fhead, checkfiles)
  1918. Xchar *out;
  1919. Xint maxlen;
  1920. Xchar *f;
  1921. XUDHEAD *fhead;
  1922. Xint checkfiles;
  1923. X{
  1924. X    char subformat[MAXSUB];
  1925. X    static int lastlen;            /* Length of last {sub}format string    */
  1926. X    int fpos = 0;                /* Position in format string            */
  1927. X    int flen = strlen(f);
  1928. X    int bool, numchars;
  1929. X    char *p, *s, *rightbr;
  1930. X
  1931. X    omaxlen = maxlen-1;    /* Make this global for convenience */
  1932. X    opos = 0;
  1933. X
  1934. X    for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
  1935. X        if (f[fpos] == BACKSLASH) {
  1936. X            fpos += doescape(out+opos, f+fpos+1);
  1937. X            opos++;
  1938. X        } else if (f[fpos] != '%')
  1939. X            out[opos++] = f[fpos];
  1940. X        else {
  1941. X            align = LEFT;
  1942. X            if (f[++fpos] == '-') {
  1943. X                align = RIGHT;
  1944. X                fpos++;
  1945. X            }
  1946. X            len = 0;
  1947. X            while (isdigit(f[fpos]))
  1948. X                len = (len * 10) + f[fpos++] - '0';
  1949. X
  1950. X            switch (f[fpos]) {
  1951. X
  1952. X                case 'a':            /* Number of file accesses */
  1953. X                    addnumber(out,fhead->accesses);
  1954. X                    break;
  1955. X
  1956. X                case 'c':            /* File comment    */
  1957. X                    addstring(out,fhead->desc);
  1958. X                    break;
  1959. X
  1960. X                case 'd':
  1961. X                    addstring(out,fhead->disk_name);
  1962. X                    break;
  1963. X
  1964. X                case 'f':
  1965. X                    if (checkfiles) {
  1966. X                        *buf = CHAR_NULL;
  1967. X                        if (fhead->online) {
  1968. X                            char ch;
  1969. X                            strcat(buf, dirnames[fhead->dirnum]);
  1970. X                            ch = buf[strlen(buf)-1];
  1971. X                            if (ch != '/' && ch != ':')
  1972. X                                strcat(buf, "/");
  1973. X                        }
  1974. X                        strcat(buf,fhead->disk_name);
  1975. X                        addstring(out,buf);
  1976. X                    } else
  1977. X                        addstring(out,fhead->disk_name); /* Maintain formatting */
  1978. X                    break;
  1979. X
  1980. X                /*
  1981. X                 *        Note that all the boolean-related options are grouped
  1982. X                 *        together, so that they can have common error handling.
  1983. X                 *        A bit nasty perhaps, but it works :-)
  1984. X                 *
  1985. X                 *        Note that 'i' and 'v' default to being online and
  1986. X                 *        valid respectively, if checkfiles hasn't been
  1987. X                 *        selected.
  1988. X                 */
  1989. X
  1990. X                case 'b':        /* True if file is binary            */
  1991. X                case 'i':        /* True if file is online            */
  1992. X                case 'l':        /* True if file uploaded locally    */
  1993. X                case 'v':        /* True if file is valid            */
  1994. X
  1995. X                    switch (f[fpos]) {
  1996. X                        case 'b':    bool = fhead->bin;
  1997. X                                    break;
  1998. X                        case 'i':    bool = checkfiles ? fhead->online : 0; 
  1999. X                                    break;
  2000. X                        case 'l':    bool = fhead->local;
  2001. X                                    break;
  2002. X                        case 'v':    bool = checkfiles ? fhead->valid : 0;
  2003. X                                    break;
  2004. X                    }
  2005. X                    /* Make sure { and } surround arguments */
  2006. X                    rightbr = strchr(f+fpos, RIGHTBR);
  2007. X                    if (f[fpos+1] == LEFTBR && rightbr != NULL) {
  2008. X                        p = f + (fpos+2);
  2009. X                        s = buf;
  2010. X
  2011. X                        if (bool) {        /* Use first argument */
  2012. X                            /* Copy until comma reached */
  2013. X                            /* Handle any escaped characters (\n, \t etc) */
  2014. X                            while (*p && *p != ',' && *p != RIGHTBR) {
  2015. X                                if (*p == BACKSLASH) {
  2016. X                                    p = p + doescape(s++, p+1) + 1;
  2017. X                                } else
  2018. X                                    *s++ = *p++;
  2019. X                            }
  2020. X                        } else {
  2021. X                            /* As above, but copy second argument */
  2022. X                            p = strchr(p,',');
  2023. X                            if (p) {
  2024. X                                p++;
  2025. X                                while (*p && *p != RIGHTBR) {
  2026. X                                    if (*p == BACKSLASH) {
  2027. X                                        p = p + doescape(s++, p+1) + 1;
  2028. X                                    } else
  2029. X                                        *s++ = *p++;
  2030. X                                }
  2031. X                            }
  2032. X                        }
  2033. X                        *s = CHAR_NULL;
  2034. X                        addstring(out,buf);
  2035. X                        /* Skip over braces */
  2036. X                        fpos = (rightbr - f);
  2037. X                    }
  2038. X                    break;
  2039. X
  2040. X                case 'k':                    /* File size in K */
  2041. X                    addnumber(out, BTOK(fhead->length));
  2042. X                    break;
  2043. X
  2044. X                case 'n':                    /* Catalogue file name of file */
  2045. X                    addstring(out, fhead->cat_name);
  2046. X                    break;
  2047. X
  2048. X                case 'o':                    /* Owner of upload */
  2049. X                    addstring(out, fhead->owner);
  2050. X                    break;
  2051. X
  2052. X                case 'p':                    /* Path name to disk file */
  2053. X                    if (checkfiles && fhead->online)
  2054. X                        addstring(out, dirnames[fhead->dirnum]);
  2055. X                    break;
  2056. X
  2057. X                case 'r':                    /* Directory number */
  2058. X                    addnumber(out, fhead->dir);
  2059. X                    break;
  2060. X
  2061. X                case 's':                    /* Section names */
  2062. X                    addstring(out, sectnums[fhead->section]);
  2063. X                    break;
  2064. X
  2065. X                case 'u':                    /* Underline with next char    */
  2066. X                    fpos++;
  2067. X                    numchars = lastlen + ((align == RIGHT) ? -len : len);
  2068. X                    while (numchars > 0 && opos < omaxlen) {
  2069. X                        out[opos++] = f[fpos];
  2070. X                        numchars--;
  2071. X                    }
  2072. X                    break;
  2073. X
  2074. X                case 'w':                    /* Date    */
  2075. X                    addstring(out,
  2076. X                      makedate( (fhead->date & 31),            /* Day        */
  2077. X                                ((fhead->date>>5)) % 13,    /* Month    */
  2078. X                                ((fhead->date>>5)) / 13));    /* Year        */
  2079. X                    break;
  2080. X
  2081. X                case 'x':
  2082. X                    addnumber(out, fhead->length);
  2083. X                    break;
  2084. X
  2085. X                case 'y':
  2086. X                    addnumber(out, fhead->dirnum);
  2087. X                    break;
  2088. X
  2089. X                case '{':                    /* Format substring in { } */
  2090. X                    {
  2091. X                        int numbrackets = 0;
  2092. X                        int savepos, savealign, savelen;
  2093. X                        p = &f[fpos + 1];
  2094. X                        /*
  2095. X                         *        Now, find the end of the substring. Nested
  2096. X                         *        brackets are skipped over.
  2097. X                         */
  2098. X                        while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
  2099. X                            switch (*p) {
  2100. X                                case LEFTBR:  numbrackets++; break;
  2101. X                                case RIGHTBR: numbrackets--; break;
  2102. X                            }
  2103. X                            p++;
  2104. X                        }
  2105. X                        if (*p == RIGHTBR) {
  2106. X                            /*
  2107. X                             *        To format the substring, we change the
  2108. X                             *        closing } into a NULL so that when
  2109. X                             *        we call format recursively, it will
  2110. X                             *        think that's the end of the string.
  2111. X                             *        After format()ing, we restore the
  2112. X                             *        bracket.
  2113. X                             */
  2114. X                            savepos   = opos;
  2115. X                            savelen   = len;
  2116. X                            savealign = align;
  2117. X                            *p = CHAR_NULL;
  2118. X                            /* Format substring */
  2119. X                            format(subformat, MAXSUB, &f[fpos + 1],
  2120. X                                                        fhead, checkfiles);
  2121. X                            *p = RIGHTBR;
  2122. X                            opos  = savepos;
  2123. X                            len   = savelen;
  2124. X                            align = savealign;
  2125. X                            addstring(out, subformat);
  2126. X                            fpos = p - f;
  2127. X                        }
  2128. X                    }
  2129. X                    break;
  2130. X
  2131. X                default:
  2132. X                    len = 0;
  2133. X                    buf[0] = '%';
  2134. X                    buf[1] = f[fpos];
  2135. X                    buf[2] = CHAR_NULL;
  2136. X                    addstring(out, buf);
  2137. X            }
  2138. X        }
  2139. X    }
  2140. X    out[opos] = CHAR_NULL;
  2141. X    lastlen = opos;
  2142. X    return (out);
  2143. X}
  2144. X
  2145. X/*
  2146. X *        echoformat()
  2147. X *        ------------
  2148. X *        This function takes a format string (as passed to the ECHO command)
  2149. X *        and uses it to produce an output string. The format string can
  2150. X *        contain the same escape sequences list under format(), and also
  2151. X *        the following special sequences:
  2152. X *
  2153. X *          %b    - Number of bytes occupied by files in last LIST command
  2154. X *          %k    - Number of kilobytes occupied by files in last LIST command
  2155. X *          %m    - Number of megabytes occupied by files in last LIST command
  2156. X *          %n    - Number of files in last LIST command
  2157. X *
  2158. X *        %B, %K, %M and %N are similar to the above except that they
  2159. X *        represent the running totals for all files listed since the
  2160. X *        last RESET command.
  2161. X *
  2162. X *          %w    - The current date, in dd-mmm-yy format.
  2163. X *          %d    - The current day (Monday, Tuesday etc.)
  2164. X *          %t    - The current time (24 hour clock)
  2165. X *          %u    - As for format()
  2166. X *          %{..}    - As for format()
  2167. X */
  2168. Xchar *echoformat(out, maxlen, f)
  2169. Xchar *out;
  2170. Xint maxlen;
  2171. Xchar *f;
  2172. X{
  2173. X    static int lastlen;
  2174. X    char subformat[MAXSUB];
  2175. X    int fpos = 0;                /* Position in format string */
  2176. X    int flen = strlen(f);
  2177. X    int value;
  2178. X    int numchars;
  2179. X    struct tm *today;
  2180. X    long seconds;
  2181. X    char *p;
  2182. X
  2183. X    /*
  2184. X     *        First of all, setup the time array
  2185. X     */
  2186. X    time(&seconds);
  2187. X    today = localtime(&seconds);
  2188. X
  2189. X    /*
  2190. X     *        Now format output string, according to format string
  2191. X     */
  2192. X    omaxlen = maxlen-1;
  2193. X    opos = 0;
  2194. X
  2195. X    for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
  2196. X        if (f[fpos] == BACKSLASH) {
  2197. X            fpos += doescape(out+opos, f+fpos+1);
  2198. X            opos++;
  2199. X        } else if (f[fpos] != '%')
  2200. X            out[opos++] = f[fpos];
  2201. X        else {
  2202. X            align = LEFT;
  2203. X            if (f[++fpos] == '-') {
  2204. X                align = RIGHT;
  2205. X                fpos++;
  2206. X            }
  2207. X            len = 0;
  2208. X            while (isdigit(f[fpos]))
  2209. X                len = (len * 10) + f[fpos++] - '0';
  2210. X
  2211. X            value = -1;
  2212. X            switch (f[fpos]) {
  2213. X
  2214. X                case 'b': value = curbytes;                    break;
  2215. X                case 'B': value = totalbytes;                break;
  2216. X                case 'k': value = BTOK(curbytes);            break;
  2217. X                case 'K': value = BTOK(totalbytes);            break;
  2218. X                case 'n': value = curfiles;                    break;
  2219. X                case 'N': value = totalfiles;                break;
  2220. X                case 'm': value = BTOK(BTOK(curbytes));        break;
  2221. X                case 'M': value = BTOK(BTOK(totalbytes));    break;
  2222. X
  2223. X                case 'd':        /* Today's day */
  2224. X                    addstring(out, days[today->tm_wday]);
  2225. X                    break;
  2226. X                case 'w':        /* Today's date */
  2227. X                    addstring(out, makedate(
  2228. X                        today->tm_mday,            /* Day        */
  2229. X                        today->tm_mon+1,        /* Month    */
  2230. X                        today->tm_year  ));        /* Year        */
  2231. X                    break;
  2232. X
  2233. X                case 't':        /* Today's time */
  2234. X                    addstring(out,
  2235. X                      maketime(today->tm_sec, today->tm_min, today->tm_hour));
  2236. X                    break;
  2237. X
  2238. X                case 'u':                    /* Underline with next char    */
  2239. X                    fpos++;
  2240. X                    numchars = lastlen + ((align == RIGHT) ? -len : len);
  2241. X                    while (numchars > 0 && opos < omaxlen) {
  2242. X                        out[opos++] = f[fpos];
  2243. X                        numchars--;
  2244. X                    }
  2245. X                    break;
  2246. X
  2247. X                /*
  2248. X                 *        Note - I'm a little unhappy about including this code
  2249. X                 *        twice, but a nice simple way of generalising it for
  2250. X                 *        both format and echoformat doesn't spring to mind.
  2251. X                 */
  2252. X                case '{':                    /* Format substring in { } */
  2253. X                    {
  2254. X                        int numbrackets = 0;
  2255. X                        int savepos, savealign, savelen;
  2256. X                        p = &f[fpos + 1];
  2257. X                        /*
  2258. X                         *        Now, find the end of the substring. Nested
  2259. X                         *        brackets are skipped over.
  2260. X                         */
  2261. X                        while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
  2262. X                            switch (*p) {
  2263. X                                case LEFTBR:  numbrackets++; break;
  2264. X                                case RIGHTBR: numbrackets--; break;
  2265. X                            }
  2266. X                            p++;
  2267. X                        }
  2268. X                        if (*p == RIGHTBR) {
  2269. X                            /*
  2270. X                             *        To format the substring, we change the
  2271. X                             *        closing } into a NULL so that when
  2272. X                             *        we call format recursively, it will
  2273. X                             *        think that's the end of the string.
  2274. X                             *        After format()ing, we restore the
  2275. X                             *        bracket.
  2276. X                             */
  2277. X                            savepos   = opos;
  2278. X                            savelen   = len;
  2279. X                            savealign = align;
  2280. X                            *p = CHAR_NULL;
  2281. X                            /* Format substring */
  2282. X                            echoformat(subformat, MAXSUB, &f[fpos + 1]);
  2283. X                            *p = RIGHTBR;
  2284. X                            opos  = savepos;
  2285. X                            len   = savelen;
  2286. X                            align = savealign;
  2287. X                            addstring(out, subformat);
  2288. X                            fpos = p - f;
  2289. X                        }
  2290. X                    }
  2291. X                    break;
  2292. X
  2293. X                default:
  2294. X                    len = 0;
  2295. X                    buf[0] = '%';
  2296. X                    buf[1] = f[fpos];
  2297. X                    buf[2] = CHAR_NULL;
  2298. X                    addstring(out, buf);
  2299. X            }
  2300. X            if (value != -1)
  2301. X                addnumber(out, value);
  2302. X        }
  2303. X    }
  2304. X    out[opos] = CHAR_NULL;
  2305. X    lastlen = opos;
  2306. X    return (out);
  2307. X}
  2308. END_OF_FILE
  2309. if test 18069 -ne `wc -c <'src/format.c'`; then
  2310.     echo shar: \"'src/format.c'\" unpacked with wrong size!
  2311. fi
  2312. # end of 'src/format.c'
  2313. fi
  2314. echo shar: End of archive 2 \(of 3\).
  2315. cp /dev/null ark2isdone
  2316. MISSING=""
  2317. for I in 1 2 3 ; do
  2318.     if test ! -f ark${I}isdone ; then
  2319.     MISSING="${MISSING} ${I}"
  2320.     fi
  2321. done
  2322. if test "${MISSING}" = "" ; then
  2323.     echo You have unpacked all 3 archives.
  2324.     rm -f ark[1-9]isdone
  2325. else
  2326.     echo You still need to unpack the following archives:
  2327.     echo "        " ${MISSING}
  2328. fi
  2329. ##  End of shell archive.
  2330. exit 0
  2331. -- 
  2332. Submissions to comp.sources.amiga and comp.binaries.amiga should be sent to:
  2333.     amiga@cs.odu.edu    
  2334. or    amiga@xanth.cs.odu.edu    ( obsolescent mailers may need this address )
  2335. or    ...!uunet!xanth!amiga    ( very obsolescent mailers need this address )
  2336.  
  2337. Comments, questions, and suggestions s should be addressed to ``amiga-request''
  2338. (only use ``amiga'' for submissions) at the above addresses.
  2339.